package cn.tedu.notice.base.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 接口限流拦截器：
 * 限流：单位时间内允许通过请求次数
 */
public class RateLimitInterceptor implements HandlerInterceptor {
    private static final int MAX_REQUESTS_PER_MINUTE = 3; // 每分钟最大请求数
    private static final long WINDOW_SIZE_SECONDS = 60; // 时间窗口大小为一分钟

    //单机情况可以使用map，分布式下将来可以使用redis
    //key为客户端ip地址,值为请求计数对象(RequestCounter)
    private final ConcurrentHashMap<String, RequestCounter> clientCounts =
            new ConcurrentHashMap<>();
    /**在preHandle方法对请求计数*/
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //获取当前请求的IP地址
        String clientId = request.getRemoteAddr();//ip
        //RequestCounter counter=clientCounts.get(clientId);
        //if(counter==null){
        //    counter=new RequestCounter();
        //    clientCounts.put(clientId,counter);
        //}
        //上面的代码可以使用computeIfAbsent方法进行替代
        RequestCounter counter = clientCounts.computeIfAbsent(clientId, k -> new RequestCounter());
        // 判断时间窗口是否已经过期
        if (counter.isExpired()) {
            counter.reset();
        }
        // 增加请求计数
        counter.increment();
        // 如果请求计数没有超过限制，则允许请求通过
        if (counter.getCount() <= MAX_REQUESTS_PER_MINUTE) {
            return true;
        }
        throw new RuntimeException("访问太频繁了");
    }
    private static class RequestCounter {
        private int count;
        private LocalDateTime lastResetTime;

        public RequestCounter() {
            this.count = 0;
            this.lastResetTime = LocalDateTime.now();
        }

        public void reset() {
            count = 0;
            lastResetTime = LocalDateTime.now();
        }
        public void increment() {
            count++;
        }
        public int getCount() {
            return count;
        }
        public boolean isExpired() {
            //当前时间是否已经超过lastResetTime+WINDOW_SIZE_SECONDS这两个时间纸之和了
            return LocalDateTime.now()
                    .isAfter(lastResetTime.plusSeconds(WINDOW_SIZE_SECONDS));
        }
    }
}
